Started: 2020-09-10
Last edited: 2020-11-25 13:41:17

library(tidyverse)

# single cell
library(Seurat)

# plotting
library(patchwork)
library(ggthemes)

1 Background

Alice had performed bulk-RNA seq on the placenta sample from COVID-19 positive mothers and control placentas and has a list of genes that were differentially expressed. Here we will use the single-cell RNA seq data to figure out in which cell types those DE genes are expressed.

2 Data

I have already annotated the clusters. The annotated data (a seurat object) was saved as .rds, which we have to read.

seur <- readRDS("../data/seurat-object_annotated.rds")
# set active assay
DefaultAssay(seur) <- "SCT"

# set levels for idents (use merged_annotations)
clust.order <- c("dec.DSC", "dec.Endo", "dec.SMC", "dec.FB", "vil.FB", "vil.EVT", "vil.SCT", "vil.VCT", "vil.Ery", "vil.Hofb", "APC", "Bcell", "Gran", "Mono_1", "Mono_2", "NK_1", "NK_2", "NK_3", "Tcell_1", "Tcell_2", "Tcell_3")

seur@meta.data$annotation_merged <- factor(seur@meta.data$annotation_merged,
                                           levels = clust.order)

# set idents
Idents(seur) <- seur@meta.data$annotation_merged

3 DE genes

Below are the DE genes sent by Alice.

genes <- list()

genes$focused <- c("HSPA1A", "PPP1R11", "LY6GLY6C", "ITGAX", "IFITM1", "C1QC", "CCL2", "OAS3", "MX1")
genes$analysis1 <- c("HSPA1A", "FMC1-LUC7L2", "HSPA1B", "AC011511.4", "PPP1R11", "AL139300.1", "LY6GLY6C")
genes$analysis2 <- c("ITGAX", "OAS3", "IFITM1", "MX1", "C1QC", "MX2", "CCL2")
genes$additional <- c("C1QTNF2", "LYVE1", "TREM1", "FOLR2", "C1QB", "CCL2", "TNFRSF10C", "CXCL9", "IL1R2", "IL36A", "CD28", "OAS1", "IL1RN", "CD36", "CXCR2",   "SERPING1", "CXCR1", "TNFRSF10C", "C1QA", "HCST", "IL36A", "IL4R", "LY96", "IL1R2", "CXCR2", "CXCL2", "S100A7", "IFITM3", "SELENOM", "SELENOP", "C3AR1", "CCL2", "CCL8")
genes$entry <- c("ACE2", "TMPRSS2", "BSG", "DPP4", "CTSL", "CTSB", "FURIN")
genes$new <- c("FCGRT", "GPX1", "GPX3", "GPX4", "DIO3", "TXNRD1", "TXNRD2", "TXNRD3")

all.genes <- c(genes$focused, genes$analysis1, genes$analysis2, genes$additional, genes$entry, genes$new) %>% 
  unique() %>% 
  sort()

4 Plots

4.1 Dotplot for all genes

# dotplot function
DotPlot2 <- function(object = seur, assay = "SCT", features, title = "", ...) {
  p <- DotPlot(object, 
               assay = assay,
               features = features, 
               dot.scale = 4, 
               dot.min = 0.01,
               ...) +
    coord_flip() +
    labs(caption = paste0("sctransform normalized expression"), title = title) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1),
          panel.grid.minor = element_blank(),
          panel.grid.major = element_line(size = 0.1),
          legend.key.size = unit(0.75, "line"))
  
  return(p)
}
DotPlot2(features = all.genes)
The following requested variables were not found: AC011511.4, AL139300.1, FMC1-LUC7L2, GPX1, LY6GLY6C

cowplot::ggsave2(last_plot(), 
                 filename = "../results/03_candidate-genes/plots/dotplot_all-genes.pdf", 
                 width = 6.5, height = 8.5, units = "in")

4.2 Individual genes

# violin function
VlnPlot2 <- function(object = seur, assay = "SCT", feature, ...) {
  p <- VlnPlot(object = object, 
               features = feature, 
               assay = assay, 
               same.y.lims = FALSE,
               split.by = "covid", 
               split.plot = TRUE,
               pt.size = 0,
               ...) + 
    coord_cartesian(clip = "off") +
    theme_classic() +
    theme(
      plot.title = element_text(size = 7, colour = "black"),
      panel.grid.minor = element_blank(),
      panel.grid.major = element_line(size = 0.2),
      axis.line = element_line(size = 0.25),
      axis.ticks = element_line(size = 0.25),
      axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, 
                                 size = 6, color = "black"),
      axis.title.x = element_blank(),
      axis.title.y = element_text(size = 6, color = "black"),
      axis.text.y = element_text(size = 6, color = "black"),
      legend.key.size = unit(0.50, "lines"),
      legend.position = c(1, 1),
      legend.direction = "horizontal",
      legend.justification = c(1, 0),
      legend.text = element_text(size = 6, colour = "black")
    )
  
  p$layers[[1]]$aes_params$size = 0.25 # violin stroke
  
  return(p)
}
# umap function
FeaturePlot2 <- function(object = seur, feature, ...) {
  DefaultAssay(object) <- "SCT"
  FeaturePlot(object, 
              feature = feature, 
              split.by = "covid",
              pt.size = 0.1, 
              order = TRUE, 
              min.cutoff = "q10", 
              combine = TRUE,
              ...) &
    plot_annotation(title = feature) &
    theme_bw() +
    theme(panel.grid.major = element_line(size = 0.25),
          panel.grid.minor = element_blank(),
          legend.key.size = unit(0.50, "line"),
          legend.text = element_text(size = 6, angle = 90, hjust = 1),
          legend.position = "bottom",
          axis.title = element_text(size = 6),
          plot.title = element_text(size = 7),
          axis.title.y.right = element_blank(),
          axis.text = element_blank(),
          aspect.ratio = 1)
}

for(i in all.genes[all.genes %in% rownames(seur)]) {
  vplot <- VlnPlot2(feature = i)
  fplot <- FeaturePlot2(feature = i, max.cutoff = "q99")
  
  cowplot::ggsave2(vplot, 
                   filename = paste0("../results/03_candidate-genes/plots/", i, "_violinplot.pdf"),
                   width = 3.5, height = 2, units = "in")
  
  cowplot::ggsave2(fplot, 
                   filename = paste0("../results/03_candidate-genes/plots/", i, "_on-umap.png"),
                   width = 3.5, height = 2.5, units = "in")
  
  vf <- vplot + fplot + 
  plot_layout(ncol = 1, widths = c(1, 0.5))
  print(vf)
}
The default behaviour of split.by has changed.
Separate violin plots are now plotted side-by-side.
To restore the old behaviour of a single split violin,
set split.plot = TRUE.
      
This message will be shown once per session.

For some genes, the umap plots and violin plots seem to disagree with each other. That is, the gene is expressed at higher level in “covid” than “control” according to the violin plot, but the colours appear stronger for “control” on umap plots. I think this is simply because of the differing number of cells from “control” and “covid”. We have fewer cells from “covid” samples (as is evident also on the umap plots), so the lightness or sparseness of blue on umap plots just reflect that. Violin plots on the other hand scale the width of violins to the total number of cells in each group, so this difference is not seen in violin plots.

5 Split dotplots

## data -----------------------------------------------------------------------
celltypes <- unique(seur$annotation_merged) %>% sort()
de.genes <- list()
for(i in celltypes){
  de.genes[[i]] <- read.csv(
    paste0("../results/04_de-genes-by-celltype/logfc_0.40/de/files/de-genes_", i, ".csv")
  )
  de.genes[[i]] <- dplyr::rename(de.genes[[i]], "gene" = "X")
  de.genes[[i]]$celltype <- i
}
## create celltype_covid ident to use for seurat dotplot ----------------------
seur$celltype_covid <- paste0(seur$annotation_merged, "_", seur$covid)
## theme for DE genes faceted dotplot -----------------------------------------
theme_dotplot <- theme(
  plot.margin = margin(5.5, 5.5, 1, 5.5),
  panel.background = element_blank(),
  text = element_text(color = "Black"),
  panel.grid = element_blank(),
  panel.border = element_rect(fill = NA, size = 0.25, color = "grey"),
  axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, size = 5, color = "Black"),
  axis.text.y = element_text(size = 5, color = "Black"),
  axis.title = element_blank(),
  axis.ticks = element_line(size = 0.25, color = "grey"),
  strip.text.x = element_text(angle = 90, hjust = 0, vjust = 0.5, 
                              size = 5.25, color = "Black"),
  strip.background = element_blank(),
  legend.title = element_text(size = 5.25),
  legend.position = "bottom",
  legend.direction = "horizontal",
  legend.box = "horizontal",
  legend.background = element_blank(),
  legend.box.spacing = unit(0.5, "lines"),
  legend.title.align = 0,
  legend.text = element_text(size = 5),
  legend.key.size = unit(0.5, "lines"),
  legend.key = element_blank(),
  panel.spacing.x = unit(0, "lines")
)
## dotplot function -----------------------------------------------------------
plot_splitdot <- function(object, features, exclude.celltypes = c()) {
  
  # idents combined for celltype and covid
  Idents(object) <- object$celltype_covid
  
  # run seurat's dotplot
  p <- Seurat::DotPlot(
    object = object, 
    assay = "SCT", 
    features = features
    )
  
  # extract plotting data
  p.dat <- p$data
  p.dat$celltype <- gsub(p.dat$id, pattern = "(.+)_([a-z]{5})", replacement = "\\1")
  p.dat$covid <- gsub(p.dat$id, pattern = "(.+)_([a-z]{5})", replacement = "\\2")
  
  # subset for celltypes of interest
  p.sub <- p.dat[!(p.dat$celltype %in% exclude.celltypes), ]
  
  # redo scaling for selected celltype_covid idents
  p.wide <- pivot_wider(p.sub,
                        id_cols = features.plot, 
                        names_from = id, 
                        values_from = avg.exp) %>% 
    as.data.frame()
  
  rownames(p.wide) <- p.wide$features.plot
  p.wide$features.plot <- NULL
  p.scaled <- t(p.wide) %>% 
    scale(center = TRUE, scale = TRUE) %>% 
    t() %>% 
    as.data.frame()
  p.scaled$features.plot <- rownames(p.scaled)
  
  # melt rescaled avg.exp
  p.scaled.long <- pivot_longer(
    data = p.scaled, 
    cols = names(p.scaled)[names(p.scaled) != "features.plot"], 
    names_to = "id", 
    values_to = "scaled.new")
  
  # cap min and max scaled value; same as col.min and col.max in Seurat::DotPlot
  p.scaled.long$scaled.new[p.scaled.long$scaled.new >  2.5] <-  2.5
  p.scaled.long$scaled.new[p.scaled.long$scaled.new < -2.5] <- -2.5
  
  # join with p.dat to get pct.exp and scaled.new in the same df
  plot.dat <- dplyr::inner_join(x = p.dat, 
                                y = p.scaled.long, 
                                by = c("features.plot", "id"))
  
  # clustering for ordering
  cdat <- pivot_wider(data =  plot.dat, id_cols = "features.plot", 
                      names_from = id,
                      values_from = scaled.new) %>% 
    as.data.frame()

  rownames(cdat) <- cdat$features.plot
  cdat$features.plot <- NULL
  cor.matrix <- cor(t(cdat))
  
  # reorder correlation matrix based on clustering
  dd <- as.dist((1 - cor.matrix))
  hc <- hclust(dd, method = 'complete')
  cdat  <- cdat[hc$order, ]

  # reorder factor levels based on clustering
  plot.dat$features.plot <- factor(plot.dat$features.plot, 
                                   levels = rownames(cdat))
  
  # prepare annotation data (hpline for DE genes)
  de.ann <- de.genes
  de.ann <- do.call(rbind, de.ann)
  de.ann <- subset(de.ann, 
                   gene %in% plot.dat$features.plot &
                     celltype %in% plot.dat$celltype
  )
  de.ann$diff.exp <- de.ann$p_val_adj < 0.05
  de.ann$diff.exp <- tolower(as.character(de.ann$diff.exp))
  
  # plot
  q <- ggplot(data = plot.dat, 
              aes(x = covid, y = features.plot)) +
    geom_point(aes(fill = scaled.new, size = pct.exp), 
               shape = 21, stroke = 0, 
               color = "White") +
    scale_fill_gradient(low = "White", 
                        high = "#d94801", 
                        name = "avg. exp. (scaled)",
                        guide = guide_colorbar(
                          title.position = "top", 
                          title.hjust = 0.5, 
                          ticks.colour = "black"
                        )
    ) +
    scale_x_discrete(breaks = c("cntrl", "covid"), 
                     labels = c("ctrl", "COVID")) +
    scale_size_area(max_size = 2.5,
                    name = "% cells with exp.",
                    guide = guide_legend(
                      override.aes = list(
                        color = "White", 
                        fill = "Grey30"
                      ),
                      title.position = "top", title.hjust = 0.5
                    )
    ) +
    facet_grid(. ~ celltype) +
    ungeviz::geom_hpline(data = de.ann,
                         aes(y = gene, x = 1.5, color = diff.exp),
                         size = 0.30, width = 1, lineend = "butt") +
    scale_color_manual(breaks = "true",
                       labels = "< 0.05",
                       values = "Black",
                       name = "DE adj.p",
                       guide = guide_legend(title.position = "top",
                                            title.hjust = 0.5)) +
    theme_dotplot
  
  return(q)
  
}
# plot new gene list
splitdot1 <- plot_splitdot(
  object = seur, 
  features = genes$new
)
The following requested variables were not found: GPX1
cowplot::ggsave2(
  splitdot1,
  filename = "../results/03_candidate-genes/plots/splitdot_set1.pdf",
  width = 3.75, height = 2, units = "in"
)

splitdot1

6 Mean expression for all genes

avg <- AverageExpression(seur, assays = "SCT") %>% .$SCT
Finished averaging SCT for cluster dec.DSC
Finished averaging SCT for cluster dec.Endo
Finished averaging SCT for cluster dec.SMC
Finished averaging SCT for cluster dec.FB
Finished averaging SCT for cluster vil.FB
Finished averaging SCT for cluster vil.EVT
Finished averaging SCT for cluster vil.SCT
Finished averaging SCT for cluster vil.VCT
Finished averaging SCT for cluster vil.Ery
Finished averaging SCT for cluster vil.Hofb
Finished averaging SCT for cluster APC
Finished averaging SCT for cluster Bcell
Finished averaging SCT for cluster Gran
Finished averaging SCT for cluster Mono_1
Finished averaging SCT for cluster Mono_2
Finished averaging SCT for cluster NK_1
Finished averaging SCT for cluster NK_2
Finished averaging SCT for cluster NK_3
Finished averaging SCT for cluster Tcell_1
Finished averaging SCT for cluster Tcell_2
Finished averaging SCT for cluster Tcell_3
avg$gene_name <- rownames(avg)
avg <- relocate(avg, gene_name)

write.csv(avg, "../results/03_candidate-genes/mean-expression_assay-sct_slot-data.csv", row.names = FALSE)

7 Session Info

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggthemes_4.2.0  patchwork_1.0.1 Seurat_3.2.2    forcats_0.5.0   stringr_1.4.0   dplyr_1.0.2     purrr_0.3.4    
 [8] readr_1.3.1     tidyr_1.1.2     tibble_3.0.4    ggplot2_3.3.2   tidyverse_1.3.0

loaded via a namespace (and not attached):
  [1] Rtsne_0.15             colorspace_2.0-0       deldir_0.1-28          ellipsis_0.3.1        
  [5] ggridges_0.5.2         fs_1.5.0               rstudioapi_0.12        spatstat.data_1.4-3   
  [9] farver_2.0.3           leiden_0.3.3           listenv_0.8.0          ggrepel_0.8.2         
 [13] fansi_0.4.1            lubridate_1.7.9        xml2_1.3.2             codetools_0.2-16      
 [17] splines_4.0.2          knitr_1.30             polyclip_1.10-0        jsonlite_1.7.1        
 [21] broom_0.7.0            ica_1.0-2              cluster_2.1.0          dbplyr_1.4.4          
 [25] png_0.1-7              uwot_0.1.8             shiny_1.5.0            sctransform_0.3.1.9002
 [29] compiler_4.0.2         httr_1.4.2             backports_1.2.0        assertthat_0.2.1      
 [33] Matrix_1.2-18          fastmap_1.0.1          lazyeval_0.2.2         cli_2.1.0             
 [37] later_1.1.0.1          htmltools_0.5.0        tools_4.0.2            rsvd_1.0.3            
 [41] igraph_1.2.5           gtable_0.3.0           glue_1.4.2             RANN_2.6.1            
 [45] reshape2_1.4.4         Rcpp_1.0.5             spatstat_1.64-1        cellranger_1.1.0      
 [49] vctrs_0.3.4            nlme_3.1-149           lmtest_0.9-38          xfun_0.19             
 [53] globals_0.13.1         rvest_0.3.6            strapgod_0.0.4.9000    mime_0.9              
 [57] miniUI_0.1.1.1         lifecycle_0.2.0        irlba_2.3.3            goftest_1.2-2         
 [61] future_1.20.1          MASS_7.3-52            zoo_1.8-8              scales_1.1.1          
 [65] hms_0.5.3              promises_1.1.1         spatstat.utils_1.17-0  parallel_4.0.2        
 [69] RColorBrewer_1.1-2     yaml_2.2.1             reticulate_1.16        pbapply_1.4-3         
 [73] gridExtra_2.3          rpart_4.1-15           stringi_1.5.3          rlang_0.4.8           
 [77] pkgconfig_2.0.3        matrixStats_0.57.0     evaluate_0.14          lattice_0.20-41       
 [81] ROCR_1.0-11            tensor_1.5             labeling_0.4.2         htmlwidgets_1.5.1     
 [85] cowplot_1.1.0          tidyselect_1.1.0       parallelly_1.21.0      RcppAnnoy_0.0.16      
 [89] plyr_1.8.6             magrittr_1.5           R6_2.5.0               generics_0.0.2        
 [93] DBI_1.1.0              pillar_1.4.6           haven_2.3.1            withr_2.3.0           
 [97] mgcv_1.8-31            fitdistrplus_1.1-1     survival_3.2-3         abind_1.4-5           
[101] future.apply_1.6.0     modelr_0.1.8           crayon_1.3.4           KernSmooth_2.23-17    
[105] plotly_4.9.2.1         ungeviz_0.1.0          rmarkdown_2.3          readxl_1.3.1          
[109] grid_4.0.2             data.table_1.13.0      blob_1.2.1             reprex_0.3.0          
[113] digest_0.6.27          xtable_1.8-4           httpuv_1.5.4           munsell_0.5.0         
[117] viridisLite_0.3.0     
LS0tCnRpdGxlOiAiRXhwcmVzc2lvbiBvZiBERSBnZW5lcyIKYXV0aG9yOiAiQXJ1biBDaGF2YW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKYmlibGlvZ3JhcGh5OiAuLi9yZWZzLmJpYgotLS0KU3RhcnRlZDogMjAyMC0wOS0xMCAgCkxhc3QgZWRpdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSlgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgc2luZ2xlIGNlbGwKbGlicmFyeShTZXVyYXQpCgojIHBsb3R0aW5nCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdndGhlbWVzKQpgYGAKCgojIEJhY2tncm91bmQKQWxpY2UgaGFkIHBlcmZvcm1lZCBidWxrLVJOQSBzZXEgb24gdGhlIHBsYWNlbnRhIHNhbXBsZSBmcm9tIENPVklELTE5IHBvc2l0aXZlIG1vdGhlcnMgYW5kIGNvbnRyb2wgcGxhY2VudGFzIGFuZCBoYXMgYSBsaXN0IG9mIGdlbmVzIHRoYXQgd2VyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQuIEhlcmUgd2Ugd2lsbCB1c2UgdGhlIHNpbmdsZS1jZWxsIFJOQSBzZXEgZGF0YSB0byBmaWd1cmUgb3V0IGluIHdoaWNoIGNlbGwgdHlwZXMgdGhvc2UgREUgZ2VuZXMgYXJlIGV4cHJlc3NlZC4gCgojIERhdGEKSSBoYXZlIGFscmVhZHkgYW5ub3RhdGVkIHRoZSBjbHVzdGVycy4gVGhlIGFubm90YXRlZCBkYXRhIChhIGBzZXVyYXRgIG9iamVjdCkgd2FzIHNhdmVkIGFzIGAucmRzYCwgd2hpY2ggd2UgaGF2ZSB0byByZWFkLiAKCmBgYHtyfQpzZXVyIDwtIHJlYWRSRFMoIi4uL2RhdGEvc2V1cmF0LW9iamVjdF9hbm5vdGF0ZWQucmRzIikKYGBgCgpgYGB7cn0KIyBzZXQgYWN0aXZlIGFzc2F5CkRlZmF1bHRBc3NheShzZXVyKSA8LSAiU0NUIgoKIyBzZXQgbGV2ZWxzIGZvciBpZGVudHMgKHVzZSBtZXJnZWRfYW5ub3RhdGlvbnMpCmNsdXN0Lm9yZGVyIDwtIGMoImRlYy5EU0MiLCAiZGVjLkVuZG8iLCAiZGVjLlNNQyIsICJkZWMuRkIiLCAidmlsLkZCIiwgInZpbC5FVlQiLCAidmlsLlNDVCIsICJ2aWwuVkNUIiwgInZpbC5FcnkiLCAidmlsLkhvZmIiLCAiQVBDIiwgIkJjZWxsIiwgIkdyYW4iLCAiTW9ub18xIiwgIk1vbm9fMiIsICJOS18xIiwgIk5LXzIiLCAiTktfMyIsICJUY2VsbF8xIiwgIlRjZWxsXzIiLCAiVGNlbGxfMyIpCgpzZXVyQG1ldGEuZGF0YSRhbm5vdGF0aW9uX21lcmdlZCA8LSBmYWN0b3Ioc2V1ckBtZXRhLmRhdGEkYW5ub3RhdGlvbl9tZXJnZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjbHVzdC5vcmRlcikKCiMgc2V0IGlkZW50cwpJZGVudHMoc2V1cikgPC0gc2V1ckBtZXRhLmRhdGEkYW5ub3RhdGlvbl9tZXJnZWQKYGBgCgojIERFIGdlbmVzCkJlbG93IGFyZSB0aGUgREUgZ2VuZXMgc2VudCBieSBBbGljZS4gCgpgYGB7cn0KZ2VuZXMgPC0gbGlzdCgpCgpnZW5lcyRmb2N1c2VkIDwtIGMoIkhTUEExQSIsICJQUFAxUjExIiwgIkxZNkdMWTZDIiwgIklUR0FYIiwgIklGSVRNMSIsICJDMVFDIiwgIkNDTDIiLCAiT0FTMyIsICJNWDEiKQpnZW5lcyRhbmFseXNpczEgPC0gYygiSFNQQTFBIiwgIkZNQzEtTFVDN0wyIiwgIkhTUEExQiIsCSJBQzAxMTUxMS40IiwgIlBQUDFSMTEiLCAiQUwxMzkzMDAuMSIsICJMWTZHTFk2QyIpCmdlbmVzJGFuYWx5c2lzMiA8LSBjKCJJVEdBWCIsICJPQVMzIiwgIklGSVRNMSIsICJNWDEiLCAiQzFRQyIsICJNWDIiLCAiQ0NMMiIpCmdlbmVzJGFkZGl0aW9uYWwgPC0gYygiQzFRVE5GMiIsICJMWVZFMSIsICJUUkVNMSIsICJGT0xSMiIsICJDMVFCIiwJIkNDTDIiLCAiVE5GUlNGMTBDIiwgIkNYQ0w5IiwgIklMMVIyIiwgIklMMzZBIiwgIkNEMjgiLCAiT0FTMSIsICJJTDFSTiIsICJDRDM2IiwgIkNYQ1IyIiwJIlNFUlBJTkcxIiwgIkNYQ1IxIiwgIlRORlJTRjEwQyIsICJDMVFBIiwgIkhDU1QiLCAiSUwzNkEiLCAiSUw0UiIsICJMWTk2IiwgIklMMVIyIiwgIkNYQ1IyIiwgIkNYQ0wyIiwgIlMxMDBBNyIsICJJRklUTTMiLCAiU0VMRU5PTSIsICJTRUxFTk9QIiwgIkMzQVIxIiwgIkNDTDIiLCAiQ0NMOCIpCmdlbmVzJGVudHJ5IDwtIGMoIkFDRTIiLCAiVE1QUlNTMiIsICJCU0ciLCAiRFBQNCIsICJDVFNMIiwgIkNUU0IiLCAiRlVSSU4iKQpnZW5lcyRuZXcgPC0gYygiRkNHUlQiLCAiR1BYMSIsICJHUFgzIiwgIkdQWDQiLCAiRElPMyIsICJUWE5SRDEiLCAiVFhOUkQyIiwgIlRYTlJEMyIpCgphbGwuZ2VuZXMgPC0gYyhnZW5lcyRmb2N1c2VkLCBnZW5lcyRhbmFseXNpczEsIGdlbmVzJGFuYWx5c2lzMiwgZ2VuZXMkYWRkaXRpb25hbCwgZ2VuZXMkZW50cnksIGdlbmVzJG5ldykgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBzb3J0KCkKYGBgCgojIFBsb3RzCiMjIERvdHBsb3QgZm9yIGFsbCBnZW5lcwoKYGBge3J9CiMgZG90cGxvdCBmdW5jdGlvbgpEb3RQbG90MiA8LSBmdW5jdGlvbihvYmplY3QgPSBzZXVyLCBhc3NheSA9ICJTQ1QiLCBmZWF0dXJlcywgdGl0bGUgPSAiIiwgLi4uKSB7CiAgcCA8LSBEb3RQbG90KG9iamVjdCwgCiAgICAgICAgICAgICAgIGFzc2F5ID0gYXNzYXksCiAgICAgICAgICAgICAgIGZlYXR1cmVzID0gZmVhdHVyZXMsIAogICAgICAgICAgICAgICBkb3Quc2NhbGUgPSA0LCAKICAgICAgICAgICAgICAgZG90Lm1pbiA9IDAuMDEsCiAgICAgICAgICAgICAgIC4uLikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGxhYnMoY2FwdGlvbiA9IHBhc3RlMCgic2N0cmFuc2Zvcm0gbm9ybWFsaXplZCBleHByZXNzaW9uIiksIHRpdGxlID0gdGl0bGUpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjEpLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjc1LCAibGluZSIpKQogIAogIHJldHVybihwKQp9CmBgYAoKYGBge3IgZmlnLmFzcD0xLjMsIGZpZy53aWR0aD02LjV9CkRvdFBsb3QyKGZlYXR1cmVzID0gYWxsLmdlbmVzKQoKY293cGxvdDo6Z2dzYXZlMihsYXN0X3Bsb3QoKSwgCiAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSAiLi4vcmVzdWx0cy8wM19jYW5kaWRhdGUtZ2VuZXMvcGxvdHMvZG90cGxvdF9hbGwtZ2VuZXMucGRmIiwgCiAgICAgICAgICAgICAgICAgd2lkdGggPSA2LjUsIGhlaWdodCA9IDguNSwgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIEluZGl2aWR1YWwgZ2VuZXMKYGBge3J9CiMgdmlvbGluIGZ1bmN0aW9uClZsblBsb3QyIDwtIGZ1bmN0aW9uKG9iamVjdCA9IHNldXIsIGFzc2F5ID0gIlNDVCIsIGZlYXR1cmUsIC4uLikgewogIHAgPC0gVmxuUGxvdChvYmplY3QgPSBvYmplY3QsIAogICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmUsIAogICAgICAgICAgICAgICBhc3NheSA9IGFzc2F5LCAKICAgICAgICAgICAgICAgc2FtZS55LmxpbXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAiY292aWQiLCAKICAgICAgICAgICAgICAgc3BsaXQucGxvdCA9IFRSVUUsCiAgICAgICAgICAgICAgIHB0LnNpemUgPSAwLAogICAgICAgICAgICAgICAuLi4pICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgdGhlbWUoCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcsIGNvbG91ciA9ICJibGFjayIpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjIpLAogICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuMjUpLAogICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA2LCBjb2xvciA9ICJibGFjayIpLAogICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIGNvbG9yID0gImJsYWNrIiksCiAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41MCwgImxpbmVzIiksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMSwgMSksCiAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAwKSwKICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYsIGNvbG91ciA9ICJibGFjayIpCiAgICApCiAgCiAgcCRsYXllcnNbWzFdXSRhZXNfcGFyYW1zJHNpemUgPSAwLjI1ICMgdmlvbGluIHN0cm9rZQogIAogIHJldHVybihwKQp9CmBgYAoKYGBge3J9CiMgdW1hcCBmdW5jdGlvbgpGZWF0dXJlUGxvdDIgPC0gZnVuY3Rpb24ob2JqZWN0ID0gc2V1ciwgZmVhdHVyZSwgLi4uKSB7CiAgRGVmYXVsdEFzc2F5KG9iamVjdCkgPC0gIlNDVCIKICBGZWF0dXJlUGxvdChvYmplY3QsIAogICAgICAgICAgICAgIGZlYXR1cmUgPSBmZWF0dXJlLCAKICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJjb3ZpZCIsCiAgICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMSwgCiAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFLCAKICAgICAgICAgICAgICBtaW4uY3V0b2ZmID0gInExMCIsIAogICAgICAgICAgICAgIGNvbWJpbmUgPSBUUlVFLAogICAgICAgICAgICAgIC4uLikgJgogICAgcGxvdF9hbm5vdGF0aW9uKHRpdGxlID0gZmVhdHVyZSkgJgogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1KSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTAsICJsaW5lIiksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICAgIGF4aXMudGl0bGUueS5yaWdodCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGFzcGVjdC5yYXRpbyA9IDEpCn0KYGBgCgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9MX0KCmZvcihpIGluIGFsbC5nZW5lc1thbGwuZ2VuZXMgJWluJSByb3duYW1lcyhzZXVyKV0pIHsKICB2cGxvdCA8LSBWbG5QbG90MihmZWF0dXJlID0gaSkKICBmcGxvdCA8LSBGZWF0dXJlUGxvdDIoZmVhdHVyZSA9IGksIG1heC5jdXRvZmYgPSAicTk5IikKICAKICBjb3dwbG90OjpnZ3NhdmUyKHZwbG90LCAKICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gcGFzdGUwKCIuLi9yZXN1bHRzLzAzX2NhbmRpZGF0ZS1nZW5lcy9wbG90cy8iLCBpLCAiX3Zpb2xpbnBsb3QucGRmIiksCiAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDMuNSwgaGVpZ2h0ID0gMiwgdW5pdHMgPSAiaW4iKQogIAogIGNvd3Bsb3Q6Omdnc2F2ZTIoZnBsb3QsIAogICAgICAgICAgICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoIi4uL3Jlc3VsdHMvMDNfY2FuZGlkYXRlLWdlbmVzL3Bsb3RzLyIsIGksICJfb24tdW1hcC5wbmciKSwKICAgICAgICAgICAgICAgICAgIHdpZHRoID0gMy41LCBoZWlnaHQgPSAyLjUsIHVuaXRzID0gImluIikKICAKICB2ZiA8LSB2cGxvdCArIGZwbG90ICsgCiAgcGxvdF9sYXlvdXQobmNvbCA9IDEsIHdpZHRocyA9IGMoMSwgMC41KSkKICBwcmludCh2ZikKfQpgYGAKCkZvciBzb21lIGdlbmVzLCB0aGUgdW1hcCBwbG90cyBhbmQgdmlvbGluIHBsb3RzIHNlZW0gdG8gZGlzYWdyZWUgd2l0aCBlYWNoIG90aGVyLiBUaGF0IGlzLCB0aGUgZ2VuZSBpcyBleHByZXNzZWQgYXQgaGlnaGVyIGxldmVsIGluICJjb3ZpZCIgdGhhbiAiY29udHJvbCIgYWNjb3JkaW5nIHRvIHRoZSB2aW9saW4gcGxvdCwgYnV0IHRoZSBjb2xvdXJzIGFwcGVhciBzdHJvbmdlciBmb3IgImNvbnRyb2wiIG9uIHVtYXAgcGxvdHMuIEkgdGhpbmsgdGhpcyBpcyBzaW1wbHkgYmVjYXVzZSBvZiB0aGUgZGlmZmVyaW5nIG51bWJlciBvZiBjZWxscyBmcm9tICJjb250cm9sIiBhbmQgImNvdmlkIi4gV2UgaGF2ZSBmZXdlciBjZWxscyBmcm9tICJjb3ZpZCIgc2FtcGxlcyAoYXMgaXMgZXZpZGVudCBhbHNvIG9uIHRoZSB1bWFwIHBsb3RzKSwgc28gdGhlIGxpZ2h0bmVzcyBvciBzcGFyc2VuZXNzIG9mIGJsdWUgb24gdW1hcCBwbG90cyBqdXN0IHJlZmxlY3QgdGhhdC4gVmlvbGluIHBsb3RzIG9uIHRoZSBvdGhlciBoYW5kIHNjYWxlIHRoZSB3aWR0aCBvZiB2aW9saW5zIHRvIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgaW4gZWFjaCBncm91cCwgc28gdGhpcyBkaWZmZXJlbmNlIGlzIG5vdCBzZWVuIGluIHZpb2xpbiBwbG90cy4KCiMgU3BsaXQgZG90cGxvdHMKYGBge3J9CiMjIGRhdGEgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KY2VsbHR5cGVzIDwtIHVuaXF1ZShzZXVyJGFubm90YXRpb25fbWVyZ2VkKSAlPiUgc29ydCgpCmRlLmdlbmVzIDwtIGxpc3QoKQpmb3IoaSBpbiBjZWxsdHlwZXMpewogIGRlLmdlbmVzW1tpXV0gPC0gcmVhZC5jc3YoCiAgICBwYXN0ZTAoIi4uL3Jlc3VsdHMvMDRfZGUtZ2VuZXMtYnktY2VsbHR5cGUvbG9nZmNfMC40MC9kZS9maWxlcy9kZS1nZW5lc18iLCBpLCAiLmNzdiIpCiAgKQogIGRlLmdlbmVzW1tpXV0gPC0gZHBseXI6OnJlbmFtZShkZS5nZW5lc1tbaV1dLCAiZ2VuZSIgPSAiWCIpCiAgZGUuZ2VuZXNbW2ldXSRjZWxsdHlwZSA8LSBpCn0KYGBgCgpgYGB7cn0KIyMgY3JlYXRlIGNlbGx0eXBlX2NvdmlkIGlkZW50IHRvIHVzZSBmb3Igc2V1cmF0IGRvdHBsb3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpzZXVyJGNlbGx0eXBlX2NvdmlkIDwtIHBhc3RlMChzZXVyJGFubm90YXRpb25fbWVyZ2VkLCAiXyIsIHNldXIkY292aWQpCmBgYAoKYGBge3J9CiMjIHRoZW1lIGZvciBERSBnZW5lcyBmYWNldGVkIGRvdHBsb3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KdGhlbWVfZG90cGxvdCA8LSB0aGVtZSgKICBwbG90Lm1hcmdpbiA9IG1hcmdpbig1LjUsIDUuNSwgMSwgNS41KSwKICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiQmxhY2siKSwKICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIHNpemUgPSAwLjI1LCBjb2xvciA9ICJncmV5IiksCiAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDUsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUsIGNvbG9yID0gIkJsYWNrIiksCiAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjI1LCBjb2xvciA9ICJncmV5IiksCiAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNS4yNSwgY29sb3IgPSAiQmxhY2siKSwKICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNS4yNSksCiAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICBsZWdlbmQuYm94ID0gImhvcml6b250YWwiLAogIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogIGxlZ2VuZC5ib3guc3BhY2luZyA9IHVuaXQoMC41LCAibGluZXMiKSwKICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLAogIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwKICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNSwgImxpbmVzIiksCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwKICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDAsICJsaW5lcyIpCikKYGBgCgpgYGB7cn0KIyMgZG90cGxvdCBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwbG90X3NwbGl0ZG90IDwtIGZ1bmN0aW9uKG9iamVjdCwgZmVhdHVyZXMsIGV4Y2x1ZGUuY2VsbHR5cGVzID0gYygpKSB7CiAgCiAgIyBpZGVudHMgY29tYmluZWQgZm9yIGNlbGx0eXBlIGFuZCBjb3ZpZAogIElkZW50cyhvYmplY3QpIDwtIG9iamVjdCRjZWxsdHlwZV9jb3ZpZAogIAogICMgcnVuIHNldXJhdCdzIGRvdHBsb3QKICBwIDwtIFNldXJhdDo6RG90UGxvdCgKICAgIG9iamVjdCA9IG9iamVjdCwgCiAgICBhc3NheSA9ICJTQ1QiLCAKICAgIGZlYXR1cmVzID0gZmVhdHVyZXMKICAgICkKICAKICAjIGV4dHJhY3QgcGxvdHRpbmcgZGF0YQogIHAuZGF0IDwtIHAkZGF0YQogIHAuZGF0JGNlbGx0eXBlIDwtIGdzdWIocC5kYXQkaWQsIHBhdHRlcm4gPSAiKC4rKV8oW2Etel17NX0pIiwgcmVwbGFjZW1lbnQgPSAiXFwxIikKICBwLmRhdCRjb3ZpZCA8LSBnc3ViKHAuZGF0JGlkLCBwYXR0ZXJuID0gIiguKylfKFthLXpdezV9KSIsIHJlcGxhY2VtZW50ID0gIlxcMiIpCiAgCiAgIyBzdWJzZXQgZm9yIGNlbGx0eXBlcyBvZiBpbnRlcmVzdAogIHAuc3ViIDwtIHAuZGF0WyEocC5kYXQkY2VsbHR5cGUgJWluJSBleGNsdWRlLmNlbGx0eXBlcyksIF0KICAKICAjIHJlZG8gc2NhbGluZyBmb3Igc2VsZWN0ZWQgY2VsbHR5cGVfY292aWQgaWRlbnRzCiAgcC53aWRlIDwtIHBpdm90X3dpZGVyKHAuc3ViLAogICAgICAgICAgICAgICAgICAgICAgICBpZF9jb2xzID0gZmVhdHVyZXMucGxvdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBpZCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYXZnLmV4cCkgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpCiAgCiAgcm93bmFtZXMocC53aWRlKSA8LSBwLndpZGUkZmVhdHVyZXMucGxvdAogIHAud2lkZSRmZWF0dXJlcy5wbG90IDwtIE5VTEwKICBwLnNjYWxlZCA8LSB0KHAud2lkZSkgJT4lIAogICAgc2NhbGUoY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKSAlPiUgCiAgICB0KCkgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpCiAgcC5zY2FsZWQkZmVhdHVyZXMucGxvdCA8LSByb3duYW1lcyhwLnNjYWxlZCkKICAKICAjIG1lbHQgcmVzY2FsZWQgYXZnLmV4cAogIHAuc2NhbGVkLmxvbmcgPC0gcGl2b3RfbG9uZ2VyKAogICAgZGF0YSA9IHAuc2NhbGVkLCAKICAgIGNvbHMgPSBuYW1lcyhwLnNjYWxlZClbbmFtZXMocC5zY2FsZWQpICE9ICJmZWF0dXJlcy5wbG90Il0sIAogICAgbmFtZXNfdG8gPSAiaWQiLCAKICAgIHZhbHVlc190byA9ICJzY2FsZWQubmV3IikKICAKICAjIGNhcCBtaW4gYW5kIG1heCBzY2FsZWQgdmFsdWU7IHNhbWUgYXMgY29sLm1pbiBhbmQgY29sLm1heCBpbiBTZXVyYXQ6OkRvdFBsb3QKICBwLnNjYWxlZC5sb25nJHNjYWxlZC5uZXdbcC5zY2FsZWQubG9uZyRzY2FsZWQubmV3ID4gIDIuNV0gPC0gIDIuNQogIHAuc2NhbGVkLmxvbmckc2NhbGVkLm5ld1twLnNjYWxlZC5sb25nJHNjYWxlZC5uZXcgPCAtMi41XSA8LSAtMi41CiAgCiAgIyBqb2luIHdpdGggcC5kYXQgdG8gZ2V0IHBjdC5leHAgYW5kIHNjYWxlZC5uZXcgaW4gdGhlIHNhbWUgZGYKICBwbG90LmRhdCA8LSBkcGx5cjo6aW5uZXJfam9pbih4ID0gcC5kYXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwLnNjYWxlZC5sb25nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImZlYXR1cmVzLnBsb3QiLCAiaWQiKSkKICAKICAjIGNsdXN0ZXJpbmcgZm9yIG9yZGVyaW5nCiAgY2RhdCA8LSBwaXZvdF93aWRlcihkYXRhID0gIHBsb3QuZGF0LCBpZF9jb2xzID0gImZlYXR1cmVzLnBsb3QiLCAKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBpZCwKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gc2NhbGVkLm5ldykgJT4lIAogICAgYXMuZGF0YS5mcmFtZSgpCgogIHJvd25hbWVzKGNkYXQpIDwtIGNkYXQkZmVhdHVyZXMucGxvdAogIGNkYXQkZmVhdHVyZXMucGxvdCA8LSBOVUxMCiAgY29yLm1hdHJpeCA8LSBjb3IodChjZGF0KSkKICAKICAjIHJlb3JkZXIgY29ycmVsYXRpb24gbWF0cml4IGJhc2VkIG9uIGNsdXN0ZXJpbmcKICBkZCA8LSBhcy5kaXN0KCgxIC0gY29yLm1hdHJpeCkpCiAgaGMgPC0gaGNsdXN0KGRkLCBtZXRob2QgPSAnY29tcGxldGUnKQogIGNkYXQgIDwtIGNkYXRbaGMkb3JkZXIsIF0KCiAgIyByZW9yZGVyIGZhY3RvciBsZXZlbHMgYmFzZWQgb24gY2x1c3RlcmluZwogIHBsb3QuZGF0JGZlYXR1cmVzLnBsb3QgPC0gZmFjdG9yKHBsb3QuZGF0JGZlYXR1cmVzLnBsb3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IHJvd25hbWVzKGNkYXQpKQogIAogICMgcHJlcGFyZSBhbm5vdGF0aW9uIGRhdGEgKGhwbGluZSBmb3IgREUgZ2VuZXMpCiAgZGUuYW5uIDwtIGRlLmdlbmVzCiAgZGUuYW5uIDwtIGRvLmNhbGwocmJpbmQsIGRlLmFubikKICBkZS5hbm4gPC0gc3Vic2V0KGRlLmFubiwgCiAgICAgICAgICAgICAgICAgICBnZW5lICVpbiUgcGxvdC5kYXQkZmVhdHVyZXMucGxvdCAmCiAgICAgICAgICAgICAgICAgICAgIGNlbGx0eXBlICVpbiUgcGxvdC5kYXQkY2VsbHR5cGUKICApCiAgZGUuYW5uJGRpZmYuZXhwIDwtIGRlLmFubiRwX3ZhbF9hZGogPCAwLjA1CiAgZGUuYW5uJGRpZmYuZXhwIDwtIHRvbG93ZXIoYXMuY2hhcmFjdGVyKGRlLmFubiRkaWZmLmV4cCkpCiAgCiAgIyBwbG90CiAgcSA8LSBnZ3Bsb3QoZGF0YSA9IHBsb3QuZGF0LCAKICAgICAgICAgICAgICBhZXMoeCA9IGNvdmlkLCB5ID0gZmVhdHVyZXMucGxvdCkpICsKICAgIGdlb21fcG9pbnQoYWVzKGZpbGwgPSBzY2FsZWQubmV3LCBzaXplID0gcGN0LmV4cCksIAogICAgICAgICAgICAgICBzaGFwZSA9IDIxLCBzdHJva2UgPSAwLCAKICAgICAgICAgICAgICAgY29sb3IgPSAiV2hpdGUiKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJXaGl0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gIiNkOTQ4MDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJhdmcuIGV4cC4gKHNjYWxlZCkiLAogICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKAogICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLmhqdXN0ID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICB0aWNrcy5jb2xvdXIgPSAiYmxhY2siCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBjKCJjbnRybCIsICJjb3ZpZCIpLCAKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiY3RybCIsICJDT1ZJRCIpKSArCiAgICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAyLjUsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIlIGNlbGxzIHdpdGggZXhwLiIsCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQoCiAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJXaGl0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gIkdyZXkzMCIKICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICB0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCB0aXRsZS5oanVzdCA9IDAuNQogICAgICAgICAgICAgICAgICAgICkKICAgICkgKwogICAgZmFjZXRfZ3JpZCguIH4gY2VsbHR5cGUpICsKICAgIHVuZ2V2aXo6Omdlb21faHBsaW5lKGRhdGEgPSBkZS5hbm4sCiAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeSA9IGdlbmUsIHggPSAxLjUsIGNvbG9yID0gZGlmZi5leHApLAogICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMzAsIHdpZHRoID0gMSwgbGluZWVuZCA9ICJidXR0IikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKGJyZWFrcyA9ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSAiPCAwLjA1IiwKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSAiQmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiREUgYWRqLnAiLAogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUuaGp1c3QgPSAwLjUpKSArCiAgICB0aGVtZV9kb3RwbG90CiAgCiAgcmV0dXJuKHEpCiAgCn0KCmBgYAoKYGBge3IgZmlnLndpZHRoPTYsIGZpZy5hc3A9MC40fQojIHBsb3QgbmV3IGdlbmUgbGlzdApzcGxpdGRvdDEgPC0gcGxvdF9zcGxpdGRvdCgKICBvYmplY3QgPSBzZXVyLCAKICBmZWF0dXJlcyA9IGdlbmVzJG5ldwopCgpjb3dwbG90OjpnZ3NhdmUyKAogIHNwbGl0ZG90MSwKICBmaWxlbmFtZSA9ICIuLi9yZXN1bHRzLzAzX2NhbmRpZGF0ZS1nZW5lcy9wbG90cy9zcGxpdGRvdF9zZXQxLnBkZiIsCiAgd2lkdGggPSAzLjc1LCBoZWlnaHQgPSAyLCB1bml0cyA9ICJpbiIKKQoKc3BsaXRkb3QxCmBgYAoKIyBNZWFuIGV4cHJlc3Npb24gZm9yIGFsbCBnZW5lcwoKYGBge3J9CmF2ZyA8LSBBdmVyYWdlRXhwcmVzc2lvbihzZXVyLCBhc3NheXMgPSAiU0NUIikgJT4lIC4kU0NUCmF2ZyRnZW5lX25hbWUgPC0gcm93bmFtZXMoYXZnKQphdmcgPC0gcmVsb2NhdGUoYXZnLCBnZW5lX25hbWUpCgp3cml0ZS5jc3YoYXZnLCAiLi4vcmVzdWx0cy8wM19jYW5kaWRhdGUtZ2VuZXMvbWVhbi1leHByZXNzaW9uX2Fzc2F5LXNjdF9zbG90LWRhdGEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCiMgU2Vzc2lvbiBJbmZvCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoK